package com.sharp.kernel.mybatis.repository;

import com.sharp.kernel.constant.db.SqlConstant;
import com.sharp.kernel.constant.err.ErrCodeSys;
import com.sharp.kernel.validate.Validator;
import com.sharp.kernel.utils.JsonUtil;
import com.sharp.kernel.utils.ReflectionUtil;
import tk.mybatis.mapper.entity.Example;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Title: ExampleFactory
 * Description: Copyright: Copyright (c) 2019 Company: BHFAE
 *
 * @author Sharp
 * @date 2022/6/10 9:55
 */
@SuppressWarnings("unchecked")
public class ExampleFactory<T> {

    protected Class<T> entityClass = (Class<T>) ReflectionUtil.getSuperClassGenericType(this.getClass(), 0);

    public Example getExample(String fieldName, Object value) {
        Example example = getExample();
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo(fieldName, value);
        return example;
    }

    public Example getExample() {
        return new Example(entityClass);
    }

    public Example getExample(T entity, String[] fieldNameArray) {
        Map<String, Object> properties = new HashMap<>(fieldNameArray.length);
        for (String field : fieldNameArray) {
            Field validField = ReflectionUtil.getField(entity.getClass(), field);
            Validator.assertNotNull(validField, ErrCodeSys.SYS_ERR_MSG, "对象属性:" + field + "不存在");
            Object value = ReflectionUtil.getValue(entity, validField);
            properties.put(field, value);
        }
        return getExample(properties);
    }

    public Example getExample(Map<String, Object> propertyMap) {
        Example example = getExample();
        Example.Criteria criteria = example.createCriteria();
        propertyMap.forEach(criteria::andEqualTo);
        return example;
    }

    public Example getExample(String propertyName, List<?> propertyList) {
        Example example = getExample();
        Example.Criteria criteria = example.createCriteria();
        criteria.andIn(propertyName, propertyList);
        return example;
    }

    @SuppressWarnings("rawtypes")
    public Example.Criteria buildCriteria(Example example, String property, Object value, Integer condition) {
        Class<?> fieldClass = ReflectionUtil.getField(entityClass, property).getType();
        if (value instanceof List) {
            value = JsonUtil.readJson(value, List.class, fieldClass);
            return buildCriteria(example, property, (List) value, condition);
        }
        value = JsonUtil.readJson(value, fieldClass);
        Example.Criteria criteria = example.createCriteria();
        if (condition == null) {
            return criteria.andEqualTo(property, value);
        }
        switch (condition) {
            case SqlConstant.NOT_EQUAL:
                return criteria.andNotEqualTo(property, value);
            case SqlConstant.GREATER:
                return criteria.andGreaterThan(property, value);
            case SqlConstant.GREATER_EQUAL:
                return criteria.andGreaterThanOrEqualTo(property, value);
            case SqlConstant.LESS:
                return criteria.andLessThan(property, value);
            case SqlConstant.LESS_EQUAL:
                return criteria.andLessThanOrEqualTo(property, value);
            case SqlConstant.NULL:
                return criteria.andIsNull(property);
            case SqlConstant.NOT_NULL:
                return criteria.andIsNotNull(property);
            case SqlConstant.LIKE:
                return criteria.andLike(property, "%" + value + "%");
            case SqlConstant.NOT_LIKE:
                return criteria.andNotLike(property, "%" + value + "%");
            case SqlConstant.NOT_EQUAL_OR_NULL:
                criteria.orNotEqualTo(property, value);
                criteria.orIsNull(property);
                return criteria;
            case SqlConstant.EQUAL:
            default:
                return criteria.andEqualTo(property, value);
        }
    }

    public Example.Criteria buildCriteria(Example example, String property, List<Object> value, Integer condition) {
        Example.Criteria criteria = example.createCriteria();
        if (condition == null) {
            return criteria.andIn(property, value);
        } else if (condition.equals(SqlConstant.NOT_IN)) {
            return criteria.andNotIn(property, value);
        } else if (condition.equals(SqlConstant.BETWEEN_OR_EQUAL)) {
            criteria.andGreaterThanOrEqualTo(property, value.get(0));
            criteria.andLessThanOrEqualTo(property, value.get(1));
            return criteria;
        } else if (condition.equals(SqlConstant.BETWEEN)) {
            criteria.andGreaterThan(property, value.get(0));
            criteria.andLessThan(property, value.get(1));
            return criteria;
        } else if (condition.equals(SqlConstant.NOT_BETWEEN_OR_EQUAL)) {
            criteria.andLessThanOrEqualTo(property, value.get(0));
            criteria.andGreaterThanOrEqualTo(property, value.get(1));
            return criteria;
        } else if (condition.equals(SqlConstant.NOT_BETWEEN)) {
            criteria.andLessThan(property, value.get(0));
            criteria.andGreaterThan(property, value.get(1));
            return criteria;
        } else {
            return criteria.andIn(property, value);
        }
    }
}
